package com.lw.lwte.common.helper;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.testng.ISuite;
import org.testng.TestNG;
import org.testng.internal.invokers.SuiteRunnerMap;
import org.testng.xml.XmlSuite;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

/**
 * testng helper
 *
 * @author lw
 **/
@Slf4j
public final class TestngHelper {
    final static String PROPERTY_KEY = "ServiceConfigFile";
    final static String DEFAULTPROPERTY = "service.properties";
    /**
     * 获取默认命令行suite
     *
     * @return
     * @throws IllegalAccessException
     */
    public static List<XmlSuite> getCmdSuiteList() {
        try {
            Object testngObj = TestNG.getDefault();
            //解析m_suites
            Field mSuites = ReflectionHelper.getDeclaredField(testngObj, "m_suites");
            mSuites.setAccessible(true);
            List<XmlSuite> xmlSuites = (List<XmlSuite>) mSuites.get(testngObj);

            return xmlSuites;
        }catch(IllegalAccessException e){
            log.error("Reflect get default TESTNG test suite failed.", e);
        }
        return null;
    }

    public static String getSuiteXmlFileName(XmlSuite xmlSuite){
        try {
            Object xmlSuiteObj = xmlSuite;
            //解析m_suites
            Field mFileName = ReflectionHelper.getDeclaredField(xmlSuiteObj, "m_fileName");
            mFileName.setAccessible(true);
            String filePath = (String) mFileName.get(xmlSuiteObj);
            File xmlFile = new File(filePath);

            return FileHelper.getNameWithoutSuffix(xmlFile);
        }catch(IllegalAccessException e){
            log.error("Reflect get m_fileName failed.", e);
        }

        return null;
    }

    /**
     * 获取suite参数信息
     *
     * @param suite
     * @return
     */
    public static Map<String, String> getSuiteParameters(XmlSuite suite) {
        Map<String, String> parameters = suite.getParameters();
        return parameters;
    }

    /**
     * 根据key获取Suite参数信息
     * @param suite
     * @param key
     * @return
     */
    public static String getSuiteParameter(XmlSuite suite, String key) {
        Map<String, String> parameters = getSuiteParameters(suite);
        if (parameters == null || parameters.isEmpty()) {
            return StringHelper.EMPTY;
        }
        if (parameters.containsKey(key)) {
            return parameters.get(key);
        } else {
            return StringHelper.EMPTY;
        }
    }

    /**
     * 获取测试Suite服务配置文件
     * @return
     */
    public static String getServicePropertyFile(){

        List<XmlSuite> suites = getCmdSuiteList();
        if(suites==null || suites.isEmpty()){
            return getServicePropertyFile(null);
        } else {
            XmlSuite suite = suites.get(0);
            return getServicePropertyFile(suite);
        }
    }

    /**
     * 获取测试Suite服务配置文件
     * @return
     */
    public static String getServicePropertyFile(XmlSuite xmlSuite){
        String configFile = StringHelper.EMPTY;

        if(xmlSuite == null){
            configFile = DEFAULTPROPERTY;
        } else {
            String serviceConfig = getSuiteParameter(xmlSuite, PROPERTY_KEY);
            if(!StringHelper.isNullOrBlank(serviceConfig)){
                configFile = serviceConfig;
            } else {
                configFile = DEFAULTPROPERTY;
            }
        }
        log.info("Load service property configuration file name: " + configFile);

        return configFile;
    }

    /**
     * 解析Xml Suite文件
     * @param suiteFile
     * @return
     */
    @SneakyThrows
    public static List<ISuite> convertSuites(String suiteFile) {
        TestNG runner = new TestNG();
        List<String> suiteFiles = new ArrayList<>();
        suiteFiles.add(suiteFile);
        runner.setTestSuites(suiteFiles);

        List listeners = new ArrayList<>();
        runner.setListenerClasses(listeners);
        runner.initializeEverything();

        Object testngObj = runner;
        Field mSuites = ReflectionHelper.getDeclaredField(testngObj, "m_suites");
        mSuites.setAccessible(true);
        List<XmlSuite> xmlSuites = (List<XmlSuite>) mSuites.get(testngObj);

        Method createMethod = filterMethod(runner.getClass(), "createSuiteRunners");
        createMethod.setAccessible(true);
        SuiteRunnerMap suiteRunnerMap = new SuiteRunnerMap();
        for (XmlSuite suite : xmlSuites) {
            createMethod.invoke(runner, suiteRunnerMap, suite);
        }

        List<ISuite> suites = new ArrayList<>();
        for (XmlSuite xmlSuite : xmlSuites) {
            ISuite suite = suiteRunnerMap.get(xmlSuite);
            //处理包含子suites场景
            List<XmlSuite> childSuites = suite.getXmlSuite().getChildSuites();
            if(childSuites.size()>0){
                SuiteRunnerMap cSuiteRunnerMap = new SuiteRunnerMap();
                for (XmlSuite cSuite : childSuites) {
                    createMethod.invoke(runner, cSuiteRunnerMap, cSuite);
                }
                for(XmlSuite cXmlSuite : childSuites){
                    ISuite cSuite = suiteRunnerMap.get(cXmlSuite);
                    suites.add(cSuite);

                }
            }else {
                suites.add(suite);
            }
        }

        return suites;
    }

    /**
     * 检查是否跳过测试案例执行流程，检查参数为“skipExecute\|skipTests”
     * @return
     */
    public static boolean skipExecute(){
        if(skipExecute == null){
            synchronized (TestngHelper.class){
                if(skipExecute == null){
                    String skipExecuteStr = System.getProperty("skipExecute");
                    String skipTestsStr = System.getProperty("skipTests");
                    if(StringHelper.isNullOrBlank(skipExecuteStr) && StringHelper.isNullOrBlank(skipExecuteStr)){
                        skipExecute = false;
                    }else{
                        if(!StringHelper.isNullOrBlank(skipExecuteStr)){
                            skipExecute = Boolean.parseBoolean(skipExecuteStr);
                        }

                        if(!StringHelper.isNullOrBlank(skipTestsStr)){
                            skipExecute = Boolean.parseBoolean(skipTestsStr);
                        }
                    }
                }
            }

            if(skipExecute == true){
                log.warn("Skip all test cases execution and collect test cases report.");
            }
        }

        return skipExecute;

    }

    public static void setSkipExecute(boolean skip){
        skipExecute = skip;
    }

    private static Boolean skipExecute;

    private static Method filterMethod(Class<?> clazz, String methodName){
        return Stream.of(clazz.getDeclaredMethods())
                .filter(o -> o.getName().equals(methodName))
                .findFirst()
                .get();
    }
}
